﻿/* Copyright (C) 2007-2010 STS Soft

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

using System;
using System.Collections.Generic;
using System.IO;
using STSdb.General.Collections;
using STSdb.General.Compression;

namespace STSdb.Persist
{
    public class Int64DeltaIndexerPersist : IIndexerPersist<Int64>
    {
        private static long[] factors = new long[] { 10000, 1000 * 10000, 60 * 1000 * 10000 };

        #region IIndexerPersist<Int64> Members

        public void Store(BinaryWriter writer, IIndexer<Int64> values)
        {
            List<long> baseRawValues = new List<long>(values.Count);
            List<long> rawValues = new List<long>(values.Count);

            int index = factors.Length - 1;
            for (int i = 0; i < values.Count; i++)
            {
                long value = values[i];
                baseRawValues.Add(value);

                //TODO: this stuff with factors must go to DateTimeIndexerPersist
                while (index >= 0)
                {
                    if (value % factors[index] == 0)
                        break;
                    else
                        index--;
                }
            }

            DeltaCompression.Helper helper = new DeltaCompression.Helper();
            if (index >= 0)
            {
                long factor = factors[index];
                for (int i = 0; i < values.Count; i++)
                {
                    baseRawValues[i] = baseRawValues[i] / factor;
                    helper.AddValue(baseRawValues[i]);
                }

                writer.Write(factor);
            }
            else
            {
                for (int i = 0; i < values.Count; i++)
                    helper.AddValue(baseRawValues[i]);

                writer.Write((long)0);
            }

            DeltaCompression.CoreCompress(writer, baseRawValues, helper);
        }

        public void Load(BinaryReader reader, IIndexer<Int64> values)
        {
            long factor = reader.ReadInt64();
            List<long> rawValues = (List<long>)DeltaCompression.CoreDecompress(reader);

            if (factor == 0)
            {
                for (int i = 0; i < values.Count; i++)
                    values[i] = rawValues[i];
            }
            else
            {
                for (int i = 0; i < values.Count; i++)
                    values[i] = factor * rawValues[i];
            }
        }

        #endregion
    }

    public class UInt64DeltaIndexerPersist : IIndexerPersist<UInt64>
    {
        public readonly Int64DeltaIndexerPersist persist = new Int64DeltaIndexerPersist();
        
        #region IIndexerPersist<T> Members

        public void Store(BinaryWriter writer, IIndexer<UInt64> values)
        {
            persist.Store(writer, new InternalIndexer(values));
        }

        public void Load(BinaryReader reader, IIndexer<UInt64> values)
        {
            persist.Load(reader, new InternalIndexer(values));
        }

        #endregion

        private class InternalIndexer : IIndexer<long>
        {
            private IIndexer<UInt64> indexer;

            public InternalIndexer(IIndexer<UInt64> indexer)
            {
                this.indexer = indexer;
            }

            #region IIndexer<long> Members

            public long this[int index]
            {
                get { return (Int64)indexer[index]; }
                set { indexer[index] = (UInt64)value; }
            }

            public int Count
            {
                get { return indexer.Count; }
            }

            #endregion
        }
    }

    public class Int32DeltaIndexerPersist : IIndexerPersist<Int32>
    {
        public readonly Int64DeltaIndexerPersist persist = new Int64DeltaIndexerPersist();

        #region IIndexerPersist<T> Members

        public void Store(BinaryWriter writer, IIndexer<Int32> values)
        {
            persist.Store(writer, new InternalIndexer(values));
        }

        public void Load(BinaryReader reader, IIndexer<Int32> values)
        {
            persist.Load(reader, new InternalIndexer(values));
        }

        #endregion

        private class InternalIndexer : IIndexer<long>
        {
            private IIndexer<Int32> indexer;

            public InternalIndexer(IIndexer<Int32> indexer)
            {
                this.indexer = indexer;
            }

            #region IIndexer<long> Members

            public long this[int index]
            {
                get { return indexer[index]; }
                set { indexer[index] = (Int32)value; }
            }

            public int Count
            {
                get { return indexer.Count; }
            }

            #endregion
        }
    }

    public class UInt32DeltaIndexerPersist : IIndexerPersist<UInt32>
    {
        public readonly Int64DeltaIndexerPersist persist = new Int64DeltaIndexerPersist();

        #region IIndexerPersist<T> Members

        public void Store(BinaryWriter writer, IIndexer<UInt32> values)
        {
            persist.Store(writer, new InternalIndexer(values));
        }

        public void Load(BinaryReader reader, IIndexer<UInt32> values)
        {
            persist.Load(reader, new InternalIndexer(values));
        }

        #endregion

        private class InternalIndexer : IIndexer<long>
        {
            private IIndexer<UInt32> indexer;

            public InternalIndexer(IIndexer<UInt32> indexer)
            {
                this.indexer = indexer;
            }

            #region IIndexer<long> Members

            public long this[int index]
            {
                get { return indexer[index]; }
                set { indexer[index] = (UInt32)value; }
            }

            public int Count
            {
                get { return indexer.Count; }
            }

            #endregion
        }
    }

    public class Int16DeltaIndexerPersist : IIndexerPersist<Int16>
    {
        public readonly Int64DeltaIndexerPersist persist = new Int64DeltaIndexerPersist();
        
        #region IIndexerPersist<T> Members

        public void Store(BinaryWriter writer, IIndexer<Int16> values)
        {
            persist.Store(writer, new InternalIndexer(values));
        }

        public void Load(BinaryReader reader, IIndexer<Int16> values)
        {
            persist.Load(reader, new InternalIndexer(values));
        }

        #endregion

        private class InternalIndexer : IIndexer<long>
        {
            private IIndexer<Int16> indexer;

            public InternalIndexer(IIndexer<Int16> indexer)
            {
                this.indexer = indexer;
            }

            #region IIndexer<long> Members

            public long this[int index]
            {
                get { return indexer[index]; }
                set { indexer[index] = (Int16)value; }
            }

            public int Count
            {
                get { return indexer.Count; }
            }

            #endregion
        }
    }

    public class UInt16DeltaIndexerPersist : IIndexerPersist<UInt16>
    {
        public readonly Int64DeltaIndexerPersist persist = new Int64DeltaIndexerPersist();
        
        #region IIndexerPersist<T> Members

        public void Store(BinaryWriter writer, IIndexer<UInt16> values)
        {
            persist.Store(writer, new InternalIndexer(values));
        }

        public void Load(BinaryReader reader, IIndexer<UInt16> values)
        {
            persist.Load(reader, new InternalIndexer(values));
        }

        #endregion

        private class InternalIndexer : IIndexer<long>
        {
            private IIndexer<UInt16> indexer;

            public InternalIndexer(IIndexer<UInt16> indexer)
            {
                this.indexer = indexer;
            }

            #region IIndexer<long> Members

            public long this[int index]
            {
                get { return indexer[index]; }
                set { indexer[index] = (UInt16)value; }
            }

            public int Count
            {
                get { return indexer.Count; }
            }

            #endregion
        }
    }

    public class ByteDeltaIndexerPersist : IIndexerPersist<Byte>
    {
        public readonly Int64DeltaIndexerPersist persist = new Int64DeltaIndexerPersist();
        
        #region IIndexerPersist<T> Members

        public void Store(BinaryWriter writer, IIndexer<Byte> values)
        {
            persist.Store(writer, new InternalIndexer(values));
        }

        public void Load(BinaryReader reader, IIndexer<Byte> values)
        {
            persist.Load(reader, new InternalIndexer(values));
        }

        #endregion

        private class InternalIndexer : IIndexer<long>
        {
            private IIndexer<Byte> indexer;

            public InternalIndexer(IIndexer<Byte> indexer)
            {
                this.indexer = indexer;
            }

            #region IIndexer<long> Members

            public long this[int index]
            {
                get { return indexer[index]; }
                set { indexer[index] = (Byte)value; }
            }

            public int Count
            {
                get { return indexer.Count; }
            }

            #endregion
        }
    }

    public class SByteDeltaIndexerPersist : IIndexerPersist<SByte>
    {
        public readonly Int64DeltaIndexerPersist persist = new Int64DeltaIndexerPersist();
        
        #region IIndexerPersist<T> Members

        public void Store(BinaryWriter writer, IIndexer<SByte> values)
        {
            persist.Store(writer, new InternalIndexer(values));
        }

        public void Load(BinaryReader reader, IIndexer<SByte> values)
        {
            persist.Load(reader, new InternalIndexer(values));
        }

        #endregion

        private class InternalIndexer : IIndexer<long>
        {
            private IIndexer<SByte> indexer;

            public InternalIndexer(IIndexer<SByte> indexer)
            {
                this.indexer = indexer;
            }

            #region IIndexer<long> Members

            public long this[int index]
            {
                get { return indexer[index]; }
                set { indexer[index] = (SByte)value; }
            }

            public int Count
            {
                get { return indexer.Count; }
            }

            #endregion
        }
    }

    public class CharDeltaIndexerPersist : IIndexerPersist<Char>
    {
        public readonly Int64DeltaIndexerPersist persist = new Int64DeltaIndexerPersist();
        
        #region IIndexerPersist<T> Members

        public void Store(BinaryWriter writer, IIndexer<Char> values)
        {
            persist.Store(writer, new InternalIndexer(values));
        }

        public void Load(BinaryReader reader, IIndexer<Char> values)
        {
            persist.Load(reader, new InternalIndexer(values));
        }

        #endregion

        private class InternalIndexer : IIndexer<long>
        {
            private IIndexer<Char> indexer;

            public InternalIndexer(IIndexer<Char> indexer)
            {
                this.indexer = indexer;
            }

            #region IIndexer<long> Members

            public long this[int index]
            {
                get { return indexer[index]; }
                set { indexer[index] = (Char)value; }
            }

            public int Count
            {
                get { return indexer.Count; }
            }

            #endregion
        }
    }
}
